package jxtend.processor.eclipse.visitor.handler.impl;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import jxtend.processor.eclipse.utils.ASTNodeKind;
import jxtend.processor.eclipse.utils.ASTNodeUtils;
import jxtend.processor.eclipse.utils.EclipseProcessorUtils;
import jxtend.processor.eclipse.visitor.handler.ASTNodeHandlerVisitorContext;
import jxtend.processor.eclipse.visitor.handler.AbstractASTNodeVisitorHandler;
import jxtend.processor.eclipse.visitor.handler.impl.CompilationResultUtils.ProblemMatcher;
import jxtend.processor.exception.JxtendProcessorException;
import jxtend.processor.reflect.ReflectField;
import jxtend.runtime.annotation.AllowSingleMemberNotation;
import jxtend.runtime.annotation.AnyAnnotation;

import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnNameOfMemberValuePair;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnSingleNameReference;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;

@SuppressWarnings("restriction")
public class GenericAnnotationHandler extends AbstractASTNodeVisitorHandler { //implements StatementTypeResolver {

	private static final ReflectField<Annotation, AnnotationBinding> compilerAnnotationOfAnnotation =
		ReflectField.getField(Annotation.class, AnnotationBinding.class, "compilerAnnotation");
	
//	@Override
//	public boolean resolveStatement(ASTNodeHandlerVisitorContext context, Statement node, Scope scope) {
//		ASTNode parentNode = context.getParentNode();
//
//		if (!(node instanceof SingleTypeReference)
//				|| !(parentNode instanceof Annotation)
//				|| (node != ((Annotation) parentNode).type)
//
//				|| (((TypeReference) node).resolvedType != null) && ((TypeReference) node).resolvedType.isValidBinding()
//
//				|| !(scope instanceof BlockScope)) {
//			return false;
//		}
//
//		Annotation annotation = (Annotation) parentNode;
//
//		CompilationUnitDeclaration compilationUnitDeclaration = scope.referenceCompilationUnit();
//		CompilationResult compilationResult = compilationUnitDeclaration.compilationResult;
//
//		// was the annotation previously resolved unsuccessfully (by compiler process; done before J-xtend is called)
//		boolean compilerAnnotationDiscoveryWasUnsuccessfull =
//			(compilationResult != null) && (compilationResult.problemCount != 0)
//				&& (annotation.resolvedType != null) && !annotation.resolvedType.isValidBinding();		
//
//
//		char[][] jxtendKeywordAnnotationCompoundName = new char[][] {
//				JxtendTypeConstants.JXTEND, JxtendTypeConstants.ANNOTATION, JxtendTypeConstants.KEYWORD,
//				((SingleTypeReference) annotation.type).token };
//
//		TypeBinding typeBinding = context.getLookupEnvironment().getType(jxtendKeywordAnnotationCompoundName);
//
//		if ((typeBinding == null) || !EclipseProcessorUtils.isValidTypeReference(typeBinding)) {
//			return false;
//		}
//
//		context.addImport(jxtendKeywordAnnotationCompoundName);
//
//
//		SingleTypeReference singleTypeReference = (SingleTypeReference) node;
//		context.resolveExpression(singleTypeReference, (BlockScope) scope);
//
//		if ((singleTypeReference.resolvedType != null)
//				&& EclipseProcessorUtils.isValidTypeReference(singleTypeReference.resolvedType)) {
//			ASTNode parentOfParentNode = context.getParentNode(1);
//
//			if (parentOfParentNode instanceof FieldDeclaration) {
//				astNodesWithEarlierUnresolvedKeywordAnnotations.add(parentOfParentNode);
//			}
//
//			if (compilerAnnotationDiscoveryWasUnsuccessfull) {
//				removeUndefinedAnnotationTypeFromCompilationResult(compilationResult, annotation);
//			}
//		}
//
//		//TODO: move following code a 'beginVisit' to the run in case 
//		//	(parentNode instanceof MakerAnnotation or NormalAnnotation) and (node instanceof 'MemberValuePair') 
//		ReferenceBinding annotationTypeBinding = (ReferenceBinding) typeBinding;
//		for (MethodBinding methodBinding : annotationTypeBinding.methods()) {
//			TypeBinding returnType = methodBinding.returnType;
//			if ((returnType == null) || !returnType.isValidBinding()) { continue; }
//			
//			boolean usesAnyAnnotation = false;
//			if (returnType instanceof ArrayBinding) {
//				TypeBinding leafComponentType = ((ArrayBinding) returnType).leafComponentType;
//
//				usesAnyAnnotation = (leafComponentType instanceof ReferenceBinding)
//					&& AnyAnnotation.class.equals(
//							EclipseProcessorUtils.findClass(((ReferenceBinding) leafComponentType).compoundName));
//			} else if (returnType instanceof ReferenceBinding) {
//				usesAnyAnnotation = AnyAnnotation.class.equals(
//						EclipseProcessorUtils.findClass(((ReferenceBinding) returnType).compoundName));
//			}
//
//			if (usesAnyAnnotation && !(annotation instanceof MarkerAnnotation)) {
//				if (annotation instanceof NormalAnnotation) {
//					MemberValuePair[] memberValuePairs = ((NormalAnnotation) annotation).memberValuePairs;
//					if (memberValuePairs != null) {
//						for (int i = 0; i < memberValuePairs.length; i++) {
//							if (Arrays.equals(memberValuePairs[i].name, methodBinding.selector)) {
//								MemberValuePair existingMemberValuePair = memberValuePairs[i];
//								
//								memberValuePairs[i] = createAnyAnnotationMemberValuePair(existingMemberValuePair.name,
//										existingMemberValuePair, existingMemberValuePair.value);
//							}
//						}
//					}
//				} else {
//					assert annotation instanceof SingleMemberAnnotation;
//					
//					SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation;
//
//					singleMemberAnnotation.memberValuePairs()[0] = createAnyAnnotationMemberValuePair(
//							TypeConstants.VALUE, singleMemberAnnotation.memberValue,
//							singleMemberAnnotation.memberValue);
//				}
//			}
//		}
//
//		return true;
//	}

	private MemberValuePair createAnyAnnotationMemberValuePair(char[] name, ASTNode sourceNode, Expression value) {
		if (sourceNode instanceof SelectionOnNameOfMemberValuePair) {
			return new SelectionOnNameOfAnyAnnotationMemberValuePair((SelectionOnNameOfMemberValuePair) sourceNode);
		}

		return new AnyAnnotationMemberValuePair(name, sourceNode.sourceStart, sourceNode.sourceEnd, value);
	}

	@Override
	public void beginVisit(ASTNodeHandlerVisitorContext context, ASTNode node, Scope scope) {
		ASTNode parentNode = context.getParentNode();

		if (!(node instanceof SingleTypeReference && parentNode instanceof Annotation
					&& node == ((Annotation) parentNode).type)
				&& !(node instanceof SingleMemberAnnotation
						&& ((SingleMemberAnnotation) node).type instanceof SingleTypeReference)) {
			return;
		}

		SingleTypeReference singleTypeReference = (node instanceof SingleTypeReference)
				? (SingleTypeReference) node : (SingleTypeReference) ((SingleMemberAnnotation) node).type;
		@SuppressWarnings("unchecked") Class<? extends java.lang.annotation.Annotation> annotationClass =
				(Class<? extends java.lang.annotation.Annotation>) findKeywordAnnotationClass(
						singleTypeReference.token);
		if (annotationClass == null) {
			return;
		}

		if (node instanceof SingleTypeReference) {
			QualifiedTypeReference newAnnotationType = ASTNodeUtils.createQualifiedTypeReference(
					ASTNodeKind.getEnum(node),
					new char[][] { JxtendTypeConstants.JXTEND, JxtendTypeConstants.ANNOTATION,
							JxtendTypeConstants.KEYWORD, ((SingleTypeReference) node).token },
					ASTNodeUtils.encodePosArray(4, node.sourceStart, node.sourceEnd));

			CompilationUnitDeclaration compilationUnitDeclaration =
					context.findParentNode(CompilationUnitDeclaration.class);
			CompilationResult compilationResult = compilationUnitDeclaration.compilationResult;

			Annotation annotation = (Annotation) parentNode;

//			// was the annotation previously resolved unsuccessfully (by compiler process; done before J-xtend is called)
//			boolean compilerAnnotationDiscoveryWasUnsuccessfull =
//				(compilationResult != null) && (compilationResult.problemCount != 0)
//					&& (annotation.resolvedType != null) && !annotation.resolvedType.isValidBinding();	
//			if (compilerAnnotationDiscoveryWasUnsuccessfull) {
				removeUndefinedAnnotationTypeFromCompilationResult(compilationResult, annotation);
				removeUndefinedAnnotationMember(compilationResult, annotation, annotationClass);
//			}

			context.replaceNode(newAnnotationType);
		} else {
			assert node instanceof SingleMemberAnnotation;

			SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) node;
			
			if (annotationClass.getAnnotation(AllowSingleMemberNotation.class) != null) {
				CompilationUnitDeclaration compilationUnitDeclaration =
						context.findParentNode(CompilationUnitDeclaration.class);
				if (compilationUnitDeclaration == null) {
					throw new JxtendProcessorException("No compilation unit could be found");
				}

				CompilationResult compilationResult = compilationUnitDeclaration.compilationResult;
			
				NormalAnnotation newAnnotation =
						new NormalAnnotation(singleMemberAnnotation.type, singleMemberAnnotation.sourceStart());
				newAnnotation.declarationSourceEnd = singleMemberAnnotation.declarationSourceEnd;

				Expression memberValue = ((SingleMemberAnnotation) singleMemberAnnotation).memberValue;

				Expression[] expressions = (memberValue instanceof ArrayInitializer)
					? ((ArrayInitializer) memberValue).expressions : new Expression[] { memberValue };

				List<MemberValuePair> newMemberValuePairs = new ArrayList<MemberValuePair>();

				for (int i = 0; i < expressions.length; i++) {
					Expression expression = expressions[i];

					if (expression instanceof SelectionOnSingleNameReference) {
						newMemberValuePairs.add(new SelectionOnNameOfMemberValuePair(
								((SelectionOnSingleNameReference) expression).token,
								expression.sourceStart, expression.sourceEnd,
								// use a 'sourceStart' and 'sourceEnd' of 'expression.sourceEnd + 1' to allow for
								// correctly showing the annotations in the selection tooltip; using unreal locations
								// like (0, 0) or (-1, -1) will not work
								new TrueLiteral(expression.sourceEnd + 1, expression.sourceEnd + 1)));
					} else if (expression instanceof SingleNameReference) {
						newMemberValuePairs.add(new MemberValuePair(((SingleNameReference) expression).token,
								expression.sourceStart, expression.sourceEnd,
								// use a 'sourceStart' and 'sourceEnd' of 'expression.sourceEnd + 1' to allow for
								// correctly showing the annotations in the selection tooltip; using unreal locations
								// like (0, 0) or (-1, -1) will not work
								new TrueLiteral(expression.sourceEnd + 1, expression.sourceEnd + 1)));
					} else if ((expression instanceof StringLiteral) && (i == 0)) {
						newMemberValuePairs.add(new MemberValuePair("name".toCharArray(),
								expression.sourceStart, expression.sourceEnd, expression));
					}

				}
				
				newAnnotation.memberValuePairs =
					newMemberValuePairs.toArray(new MemberValuePair[newMemberValuePairs.size()]);

				removeUndefinedValueAnnotationMember(compilationResult, singleMemberAnnotation);
				removeUnresolvedVariableForAnnotationMember(compilationResult, singleMemberAnnotation, annotationClass);
				removeUndefinedAnnotationMember(compilationResult, singleMemberAnnotation, annotationClass);

				context.replaceNode(newAnnotation);
			}
		}
	}

	private static Class<?> findKeywordAnnotationClass(char[] token) {
		String className = new StringBuilder().append(JxtendTypeConstants.JXTEND)
			.append('.').append(JxtendTypeConstants.ANNOTATION)
			.append('.').append(JxtendTypeConstants.KEYWORD)
			.append('.').append(token)
			.toString();

		try {
			return GenericAnnotationHandler.class.getClassLoader().loadClass(className);
		} catch (ClassNotFoundException e) {
			return null;
		} catch (NoClassDefFoundError e) {
			return null;
		}
	}
	
	@Override
	public boolean endVisitBeforeResolve(ASTNodeHandlerVisitorContext context, ASTNode node, Scope scope) {
		if (!(node instanceof Annotation)) {
			return true;
		}

		Annotation annotation = (Annotation) node;

		CompilationUnitDeclaration compilationUnitDeclaration =
			context.findParentNode(CompilationUnitDeclaration.class);
		if (compilationUnitDeclaration == null) {
			throw new JxtendProcessorException("No compilation unit could be found");
		}

		//TODO: look at this if statement; doesn't make sense any more
		if ((annotation.type == null) || (annotation.type.resolvedType == null)
				|| !EclipseProcessorUtils.isValidTypeReference(annotation.type.resolvedType)) {
			return false;
		}

//		CompilationResult compilationResult = compilationUnitDeclaration.compilationResult;
//
//		ReferenceBinding resolvedAnnotationType = (ReferenceBinding) annotation.type.resolvedType;
//
//		char[][] annotationCompoundName = resolvedAnnotationType.compoundName;
//		@SuppressWarnings("unchecked") Class<? extends java.lang.annotation.Annotation> annotationClass =
//			(Class<? extends java.lang.annotation.Annotation>) EclipseProcessorUtils.findClass(annotationCompoundName);
//		if (annotationClass == null) {
//			return true;
//		}
//
//		if (annotation.type instanceof QualifiedTypeReference
//				&& isJxtendKeywordAnnotationType(((QualifiedTypeReference) annotation.type).tokens)) {
//			removeUndefinedAnnotationMember(compilationResult, annotation, annotationClass);
//		}
//
//		if (annotation instanceof NormalAnnotation) {
//			MemberValuePair[] memberValuePairs = ((NormalAnnotation) annotation).memberValuePairs;
//			for (int i = 0; i < memberValuePairs.length; i++) {
//				removeUndefinedAnnotationMember(compilationResult, annotation, annotationClass);
//			}
//		}
//
//		if ((annotation instanceof SingleMemberAnnotation)
//				&& (annotationClass.getAnnotation(AllowSingleMemberNotation.class) != null)) {
//			removeUndefinedValueAnnotationMember(compilationResult, annotation);
//			removeUnresolvedVariableForAnnotationMember(compilationResult, annotation, annotationClass);
//
//			NormalAnnotation newAnnotation = new NormalAnnotation(annotation.type, annotation.sourceStart());
//			newAnnotation.declarationSourceEnd = annotation.declarationSourceEnd;
//
//			Expression memberValue = ((SingleMemberAnnotation) annotation).memberValue;
//
//			Expression[] expressions = (memberValue instanceof ArrayInitializer)
//				? ((ArrayInitializer) memberValue).expressions : new Expression[] { memberValue };
//
//			List<MemberValuePair> newMemberValuePairs = new ArrayList<MemberValuePair>(expressions.length);
//			for (int i = 0; i < expressions.length; i++) {
//				Expression expression = expressions[i];
//
//				if (expression instanceof SelectionOnSingleNameReference) {
//					newMemberValuePairs.add(new SelectionOnNameOfMemberValuePair(
//							((SelectionOnSingleNameReference) expression).token,
//							expression.sourceStart, expression.sourceEnd,
//							// use a 'sourceStart' and 'sourceEnd' of 'expression.sourceEnd + 1' to allow for
//							// correctly showing the annotations in the selection tooltip; using unreal locations
//							// like (0, 0) or (-1, -1) will not work
//							new TrueLiteral(expression.sourceEnd + 1, expression.sourceEnd + 1)));
//				} else if (expression instanceof SingleNameReference) {
//					newMemberValuePairs.add(new MemberValuePair(((SingleNameReference) expression).token,
//							expression.sourceStart, expression.sourceEnd,
//							// use a 'sourceStart' and 'sourceEnd' of 'expression.sourceEnd + 1' to allow for
//							// correctly showing the annotations in the selection tooltip; using unreal locations
//							// like (0, 0) or (-1, -1) will not work
//							new TrueLiteral(expression.sourceEnd + 1, expression.sourceEnd + 1)));
//				} else if ((expression instanceof StringLiteral) && (i == 0)) {
//					newMemberValuePairs.add(new MemberValuePair("name".toCharArray(),
//							expression.sourceStart, expression.sourceEnd, expression));
//				}
//			}
//			
//			newAnnotation.memberValuePairs =
//				newMemberValuePairs.toArray(new MemberValuePair[newMemberValuePairs.size()]);
//
//			context.replaceNode(newAnnotation);
//			
//			toBeResolvedAnnotation = newAnnotation;
//		}

		//TODO: move following code a 'beginVisit' to the run in case 
		//	(parentNode instanceof MakerAnnotation or NormalAnnotation) and (node instanceof 'MemberValuePair') 
		if (node instanceof SingleMemberAnnotation || node instanceof NormalAnnotation) {
			ReferenceBinding annotationTypeBinding = (ReferenceBinding) annotation.type.resolvedType;
		
			for (MethodBinding methodBinding : annotationTypeBinding.methods()) {
				TypeBinding returnType = methodBinding.returnType;
				if ((returnType == null) || !returnType.isValidBinding()) { continue; }
			
				boolean usesAnyAnnotation = false;
				if (returnType instanceof ArrayBinding) {
					TypeBinding leafComponentType = ((ArrayBinding) returnType).leafComponentType;

					usesAnyAnnotation = (leafComponentType instanceof ReferenceBinding)
						&& AnyAnnotation.class.equals(
								EclipseProcessorUtils.findClass(((ReferenceBinding) leafComponentType).compoundName));
				} else if (returnType instanceof ReferenceBinding) {
					usesAnyAnnotation = AnyAnnotation.class.equals(
							EclipseProcessorUtils.findClass(((ReferenceBinding) returnType).compoundName));
				}

				if (usesAnyAnnotation && !(annotation instanceof MarkerAnnotation)) {
					if (annotation instanceof NormalAnnotation) {
						MemberValuePair[] memberValuePairs = ((NormalAnnotation) annotation).memberValuePairs;
						if (memberValuePairs != null) {
							for (int i = 0; i < memberValuePairs.length; i++) {
								if (Arrays.equals(memberValuePairs[i].name, methodBinding.selector)) {
									MemberValuePair existingMemberValuePair = memberValuePairs[i];

									//TODO: use context.replaceNode; for this node must be "instanceof MemberValuePair"
									memberValuePairs[i] = createAnyAnnotationMemberValuePair(
											existingMemberValuePair.name, existingMemberValuePair,
											existingMemberValuePair.value);
								}
							}
						}
					} else {
						assert annotation instanceof SingleMemberAnnotation;
					
						SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation;

						//TODO: use context.replaceNode; for this node must be "instanceof MemberValuePair"
						singleMemberAnnotation.memberValuePairs()[0] = createAnyAnnotationMemberValuePair(
										TypeConstants.VALUE, singleMemberAnnotation.memberValue,
										singleMemberAnnotation.memberValue);
					}
				}
			}
		}

		
		compilerAnnotationOfAnnotation.set(annotation, null);

		annotation.resolvedType = null;
		annotation.type.resolvedType = null;

		ASTNode parentNode = context.getParentNode();

		Binding annotationRecipient = null;
		if (parentNode instanceof AbstractMethodDeclaration) {
			annotationRecipient = ((AbstractMethodDeclaration) parentNode).binding;
		} else if ((parentNode instanceof Argument)
				&& (context.getParentNode(1) instanceof AbstractMethodDeclaration)) {
			Argument argument = (Argument) parentNode;

			annotationRecipient =
				new LocalVariableBinding(argument.name, argument.type.resolvedType, argument.modifiers, true); 
		} else if (parentNode instanceof FieldDeclaration) {
			annotationRecipient = ((FieldDeclaration) parentNode).binding;
		} else if (parentNode instanceof TypeDeclaration) {
			annotationRecipient = ((TypeDeclaration) parentNode).binding;
		} else if (parentNode instanceof LocalDeclaration) {
			LocalDeclaration localDeclaration = (LocalDeclaration) parentNode;

			annotationRecipient = new LocalVariableBinding(
					localDeclaration.name, localDeclaration.type.resolvedType, localDeclaration.modifiers, false); 
		}

		annotation.recipient = annotationRecipient;

		return true;			
	}

	private static void removeUndefinedAnnotationTypeFromCompilationResult(
			CompilationResult compilationResult, final Annotation annotation) {
		assert (annotation != null) && (annotation.type instanceof SingleTypeReference);

		CompilationResultUtils.removeProblemsFromCompilationResult(compilationResult, true,
				new ProblemMatcher(IProblem.UndefinedType, 1) {
					@Override protected boolean doMatchProblem(IProblem problem) {
						return (problem.getSourceStart() == annotation.type.sourceStart)
							&& (problem.getSourceEnd() == annotation.type.sourceEnd)
							&& String.valueOf(((SingleTypeReference) annotation.type).token).equals(
									problem.getArguments()[0]);
					}
				});
	}

	private static void removeUndefinedValueAnnotationMember(
			CompilationResult compilationResult, final Annotation annotation) {
		assert (annotation != null) && (annotation.type != null) && (annotation.type.resolvedType != null);

		CompilationResultUtils.removeProblemsFromCompilationResult(compilationResult, true,
				new ProblemMatcher(IProblem.UndefinedAnnotationMember, 2) {
					@Override protected boolean doMatchProblem(IProblem problem) {
						return (problem.getSourceStart() > annotation.sourceStart)
							&& (problem.getSourceEnd() < annotation.declarationSourceEnd)
							&& "value".equals(problem.getArguments()[0])
							&& (String.valueOf(annotation.type.resolvedType.readableName()).equals(
										problem.getArguments()[1])
									|| String.valueOf(annotation.type.resolvedType.shortReadableName()).equals(
											problem.getArguments()[1]));
					}
				});
	}

	private static void removeUnresolvedVariableForAnnotationMember(CompilationResult compilationResult,
			final Annotation annotation, Class<? extends java.lang.annotation.Annotation> annotationClass) {
		assert annotationClass != null;

		final Set<String> annotationMemberNames = getAnnotationMembers(annotationClass);

		CompilationResultUtils.removeProblemsFromCompilationResult(compilationResult, false,
				new ProblemMatcher(IProblem.UnresolvedVariable, 1) {
					@Override protected boolean doMatchProblem(IProblem problem) {
						return (problem.getSourceStart() > annotation.sourceStart)
							&& (problem.getSourceEnd() < annotation.declarationSourceEnd)
							&& annotationMemberNames.contains(problem.getArguments()[0]);
					}
				});
	}

	private static void removeUndefinedAnnotationMember(
			CompilationResult compilationResult, final Annotation annotation,
			Class<? extends java.lang.annotation.Annotation> annotationClass) {
		assert annotationClass != null;

		final Set<String> annotationMemberNames = getAnnotationMembers(annotationClass);

		CompilationResultUtils.removeProblemsFromCompilationResult(compilationResult, false,
				new ProblemMatcher(IProblem.UndefinedAnnotationMember, 2) {
					@Override protected boolean doMatchProblem(IProblem problem) {
						return (problem.getSourceStart() > annotation.sourceStart)
							&& (problem.getSourceEnd() < annotation.declarationSourceEnd)
							&& annotationMemberNames.contains(problem.getArguments()[0])
							&& (String.valueOf(annotation.type.resolvedType.readableName()).equals(
										problem.getArguments()[1])
									|| String.valueOf(annotation.type.resolvedType.shortReadableName()).equals(
											problem.getArguments()[1]));
					}
				});		
	}
	
	private static <T extends java.lang.annotation.Annotation> Set<String> getAnnotationMembers(
			Class<T> annotationClass) {
		assert (annotationClass != null) && !annotationClass.equals(Annotation.class);

		Set<String> result = new HashSet<String>();

		for (Method method : annotationClass.getMethods()) {
			if (!method.getDeclaringClass().equals(annotationClass)) { continue; }

			result.add(method.getName());
		}

		return result;
	}

	@Override
	public void endVisitAfterResolve(ASTNodeHandlerVisitorContext context, ASTNode node, Scope scope) {
		if (!Thread.currentThread().getName().equals("Text Viewer Hover Presenter")
				|| !(node instanceof FieldDeclaration)
				|| (((FieldDeclaration) node).binding == null)
				|| !(((FieldDeclaration) node).binding.declaringClass instanceof SourceTypeBinding)
				|| !EclipseProcessorUtils.isValidTypeReference(((FieldDeclaration) node).binding.declaringClass)
				|| (((FieldDeclaration) node).binding.declaringClass instanceof ParameterizedTypeBinding)) {
			return;
		}

		FieldDeclaration fieldDeclaration = (FieldDeclaration) node;
		ReferenceBinding declaringClass = fieldDeclaration.binding.declaringClass;

		((SourceTypeBinding) declaringClass).resolveTypeFor(fieldDeclaration.binding);
		
//		FieldDeclaration fieldDeclaration = (FieldDeclaration) node;
//
//		if (fieldDeclaration.binding != null) {
//			fieldDeclaration.binding.declaringClass.getField(fieldDeclaration.name, true);
//		}

//		List<AnnotationBinding> annotationBindingList = new ArrayList<AnnotationBinding>();
//		for (Annotation annotation : fieldDeclaration.annotations) {
//			if ((annotation.getCompilerAnnotation() == null) || !EclipseProcessorUtils.isValidTypeReference(
//							annotation.getCompilerAnnotation().getAnnotationType())) { continue; }
//
//			annotationBindingList.add(annotation.getCompilerAnnotation());
//		}
//
//		fieldDeclaration.binding.setAnnotations(
//				annotationBindingList.toArray(new AnnotationBinding[annotationBindingList.size()]));
//
//		System.out.println();
	}

}
